//
//  Common+PPKit.swift
//  PPKit
//
//  Created by xiaopin on 16/8/31.
//  Copyright © 2016年 pinguo. All rights reserved.
//  https://gitee.com/xiaopindev/PPKit
//  https://github.com/xiaopindev
//  https://xiaopin.cnblogs.com

import UIKit
import WebKit
import StoreKit
import KeychainSwift
import CoreTelephony

/*
 PPKit索引：
 pp_App*        【应用程序】 属性和操作
 pp_Device*     【设备】 属性和操作
 pp_Bundle*     【本地资源】 属性和操作
 pp_json*       【JSON】 操作
 pp_dispatch*   【GCD】 操作
 pp_sandbox*    【沙盒】 属性和操作
 pp_archive*    【归档文件名】
 pp_screen*     【屏幕】 属性和操作
 pp_const*      【常量】 属性和操作
 pp_font        【自定义字体】操作
 pp_color       【深浅色颜色】操作
 pp_image*      【图片格式化等】操作
 */

//MARK: 0.pp_App,pp_Device: 应用设备信息
/*
 platform: android , apple
 deviceModel : iPhone | iPad |  Apple Watch | Mac | Apple TV
 deviceModelName: iPhone 7 | iPhone X | iPhone Max | iPad XXXX | ... | ....
 deviceSystemName: iOS | iPadOS | WatchOS | MacOS | tvOS
 deviceSystemVersion: 14.0 | 14.0 | 7.0 | 14.15.12 | 13.0
 **/
/// App 名称
let pp_AppDisplayName: String = Bundle.main.infoDictionary!["CFBundleDisplayName"] as! String
/// Bundle Identifier
let pp_AppBundleIdentifier:String = Bundle.main.bundleIdentifier!
/// App 版本号
let pp_AppVersion:String = Bundle.main.infoDictionary! ["CFBundleShortVersionString"] as! String
/// 应用 Bulid 版本号
let pp_AppBuildVersion : String = Bundle.main.infoDictionary! ["CFBundleVersion"] as! String
/// 设备 udid
let pp_DeviceUUID = UIDevice.current.identifierForVendor!.uuidString
/// 系统名称
let pp_DeviceSystemName = UIDevice.current.systemName
/// 系统版本
let pp_DeviceSystemVersion: String = UIDevice.current.systemVersion
/// 设备型号
let pp_DeviceModel = UIDevice.current.model
/// 设备具体型号
let pp_DeviceModelName = UIDevice.current.modelName
/// 设备区域化型号
let pp_DeviceLocalizedModel = UIDevice.current.localizedModel
/// 设备主题模式
let pp_DeviceThemeMode = UITraitCollection.current.userInterfaceStyle

/// 钥匙串Key
let pp_AppKeychainNameKey = "\(pp_AppBundleIdentifier).ServiceKey"
let pp_AppUUIDKeychainNameKey = "\(pp_AppBundleIdentifier).UUIDKey"

//MARK: 1.pp_const,pp_archive: 常用常量,归档常量
/// 第一次启动
let pp_const_FirstLaunch = "pp_const_FirstLaunch"
/// 应用图标名称
let pp_const_iconName = "pp_const_iconName"
/// 运行环境
let pp_const_runEnv = "pp_const_runEnv"
/// 是否已经同意过隐私条款
let pp_const_isAgreedPrivacy = "pp_const_isAgreedPrivacy"
/// 是否登录
let pp_const_isLogin = "pp_const_isLogin"
/// 登录方式
let pp_const_logintype = "pp_const_logintype"
/// 是否是第三方登录
let pp_const_isThirdLogin = "pp_const_isThirdLogin"
/// 语言包信息存储key
let pp_const_LanguagesKey = "pp_const_LanguagesKey"
/// 语言存储key
let pp_const_AppLanguageKey = "pp_const_AppLanguageKey"
/// DB语言存储key
let pp_const_DBAppLanguageKey = "pp_const_DBAppLanguageKey"
/// wifi显示图片key
let pp_const_AppLoadPicInWifiKey = "pp_const_AppLoadPicInWifiKey"
/// wifi播放视频key
let pp_const_AppPlayeVideoInWifiKey = "pp_const_AppPlayeVideoInWifiKey"
/// 主题模式
let pp_const_thememode = "pp_const_thememodes"
/// 提醒设置模式
let pp_const_msgwarnmode = "pp_const_msgwarnmode"
/// 登录用户归档文件名
let pp_archive_LoginUserFileName = "pp_archive_LoginUser.archive"

let pp_attrkey_font = NSAttributedString.Key.font
let pp_attrkey_link = NSAttributedString.Key.link
let pp_attrkey_attachment = NSAttributedString.Key.attachment
let pp_attrkey_paragraphStyle = NSAttributedString.Key.paragraphStyle
let pp_attrkey_foregroundColor = NSAttributedString.Key.foregroundColor
let pp_attrkey_backgroundColor = NSAttributedString.Key.backgroundColor
let pp_attrkey_underlineStyle = NSAttributedString.Key.underlineStyle
let pp_attrkey_underlineColor = NSAttributedString.Key.underlineColor
let pp_attrkey_deletelineStyle = NSAttributedString.Key.strikethroughStyle

//MARK: 2.机型判断

/// 当前设备是否是iPhone
let isPhone = UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.phone
/// 当前设备是否是iPad
let isPad = UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.pad

//MARK: 3.屏幕相关
/// 屏幕的宽
let pp_screen_width = UIDevice.current.screenWidth()
/// 屏幕的高
let pp_screen_height = UIDevice.current.screenHeight()

/*
 
 iPhone各屏幕状态栏高度（含iPhone15系列）
 https://www.jianshu.com/p/bd2f684af0e3
 
 官方布局指南
 https://developer.apple.com/cn/design/human-interface-guidelines/layout
 
 iPhone系列：
 屏幕尺寸       像素      留底广告尺寸  倍数   对应机型
 430x932    1290x2796   0000x0000   3x    14ProMax,15ProMax,15Plus 灵动岛
 393x852    1179x2556   0000x0000   3x    14Pro,15,15Pro 灵动岛
 428x926    1284x2778   0000x0000   3x    12ProMax,13ProMax,14Plus
 390x844    1170x2532   1170x2232   3x    12,12Pro,13,13Pro,14
 375x812    1125x2436   1125x2040   3x    X,XS,11Pro,12Mini,13Mini
 414x896    1242x2688   1242x2283   23x   XR,XSMax,11,11ProMax
 414x736    1242x2208   1242x1821   3x    678Plus系列
 375x667    750x1334    750x1090    2x    678系列,SE23
 
 淘汰iPhone：
 320x568    640x1136    5,SE1
 320x480    640x960     4
 
 iPad系列：
 1024x1366  2048x2732   2x      12.9 iPad Pro
 834x1194   1668x2388   2x      10.5,11 iPad Pro
 744x1133   1488x2266   2x      8.3 iPadMini
 820x1180   1640x2360   2x      10.9 iPad Air
 834x1112   1668x2224   2x      10.5 iPad Air
 810x1080   1620x2160   2x      10.2 iPad
 768x1024   1536x2048   2x      7.9 iPadMini,9.7 iPad,iPadAir,Pro
 **/
/// iPhone6，7，8，iPhone SE 2代,iPhoneX,iPhone11Pro
let pp_screen_w_375 = UIScreen.main.bounds.size.width == 375.0 ? true:false
/// iPhone12 ,iPhone 12 Pro
let pp_screen_w_390 = UIScreen.main.bounds.size.width == 390.0 ? true:false
/// iPhone6,7,8 Plus,iPhoneXR, iPhoneXsMax, iPhone11ProMax
let pp_screen_w_414 = UIScreen.main.bounds.size.width == 414.0 ? true:false
/// iPhone 12ProMax
let pp_screen_w_428 = UIScreen.main.bounds.size.width == 428.0 ? true:false

let pp_screen_h_480 = UIScreen.main.bounds.size.height == 480.0 ? true:false
let pp_screen_h_568 = UIScreen.main.bounds.size.height == 568.0 ? true:false
let pp_screen_h_667 = UIScreen.main.bounds.size.height == 667.0 ? true:false
let pp_screen_h_736 = UIScreen.main.bounds.size.height == 736.0 ? true:false
let pp_screen_h_780 = UIScreen.main.bounds.size.height == 780.0 ? true:false
let pp_screen_h_812 = UIScreen.main.bounds.size.height == 812.0 ? true:false
let pp_screen_h_844 = UIScreen.main.bounds.size.height == 844.0 ? true:false
let pp_screen_h_896 = UIScreen.main.bounds.size.height == 896.0 ? true:false
let pp_screen_h_926 = UIScreen.main.bounds.size.height == 926.0 ? true:false

/// iPhone6，7，8，iPhone SE 2代
let pp_screen_375x667 = (pp_screen_w_375 && pp_screen_h_667)
/// iPhoneX，iPhoneXS，iPhone11Pro
let pp_screen_375x812 = (pp_screen_w_375 && pp_screen_h_812)
/// iPhone12 ,iPhone 12 Pro
let pp_screen_390x844 = (pp_screen_w_390 && pp_screen_h_844)
/// iPhone6Plus，7Plus，8Plus
let pp_screen_414x736 = (pp_screen_w_414 && pp_screen_h_736)
/// iPhoneXR，iPhoneXSMax，iPhone11, iPhone11ProMax,
let pp_screen_414x896 = (pp_screen_w_414 && pp_screen_h_896)
/// iPhone 12ProMax
let pp_screen_428x926 = (pp_screen_w_428 && pp_screen_h_926)

/// 是否有刘海
let pp_screen_HasBang = (pp_screen_375x812 || pp_screen_414x896 || pp_screen_390x844 || pp_screen_428x926)

/// 宽度比例
let pp_widthRatio = pp_screen_width / 375.0

/// 手机屏幕 #状态栏# 的高度
let pp_screen_statusbar_height:CGFloat = pp_screen_HasBang ? 44.0:20.0
/// 手机屏幕 #导航栏# 的高度
let pp_screen_navbar_height:CGFloat = pp_screen_HasBang ? 88.0:64.0
/// 手机屏幕 #底部工具栏# 的高度
let pp_screen_tabbar_height:CGFloat = pp_screen_HasBang ? 83.0:49.0
/// 手机屏幕 底部工具栏下面的高度，主要是针对iPhoneX系列，其他系列都是0
let pp_screen_tabbar_bottom_height:CGFloat = pp_screen_HasBang ? 34.0:0.0

//MARK: 4.iOS版本判断
//Swift推荐写法：if #available(iOS 10.0, *) {}
//let iOS10Later = ProcessInfo.processInfo.isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 10, minorVersion: 0, patchVersion: 0))
//let iOS11Later = ProcessInfo.processInfo.isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 11, minorVersion: 0, patchVersion: 0))
//let iOS12Later = ProcessInfo.processInfo.isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 12, minorVersion: 0, patchVersion: 0))
//let iOS13Later = ProcessInfo.processInfo.isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 13, minorVersion: 0, patchVersion: 0))

let isMessengerInstalled = UIApplication.shared.canOpenURL(URL(string: "fb-messenger-share-api://")!)
let isTwitterInstalled = UIApplication.shared.canOpenURL(URL(string: "twitter://")!)

//MARK: 5.Block统一格式
/// 请求进度，progress进度数
public typealias ProgressHandler = ((_ progress:Progress) -> ())
/// 请求文件下载进度，参数值自动计算大小和单位转换
public typealias DownloadProgressHandler = ((_ progress:Progress, _ percentStr: String? , _ loadedSizeStr: String? ) -> ())
/// 请求文件下载进度，参数值自动计算大小和单位转换
public typealias UploadProgressHandler = ((_ progress:Progress, _ percentStr: String? , _ loadedSizeStr: String?) -> ())
/// 请求完成，msg: XXX_YYY_ZZZ格式，res : responseObject返回的json
public typealias FinishedHandler = ((_ msg:String?,_ count:Int?, _ res:Any?) -> ())
/// 请求失败，msg: XXX_YYY_ZZZ格式，code ：errorCode需要与API约定
public typealias FailedHandler = ((_ msg:String? ,_ code:Int?) -> ())
/*
  errorCode说明：
  0：系统错误信息
  大于0的为后台自定义信息
 */

//MARK: 6.常用方法
/// 输出日志
///
/// - Parameters:
///   - message: 消息体
///   - file: 文件
///   - funcName: 方法名
///   - line: 行号
func PPLog<T>(_ message:T,file:String = #file,funcName:String = #function,line:Int = #line){
    #if DEBUG
    let fileName = (file as NSString).lastPathComponent
    
    let consoleStr = "[PPLog][\(Date().toString())]-\(fileName)>\(funcName):(\(line))-🏀\(message)⚽️"
    print(consoleStr)
    #endif
}

/// Socket输入日志
func SocketLog<T>(_ message:T,file:String = #file,function:String = #function,line:Int = #line){
    let fileName = (file as NSString).lastPathComponent
#if DEBUG
    //日志内容
    let consoleStr = "[SocketLog][\(Date().toString())]-\(fileName):(\(line))-🏀\(message)⚽️"
    let postStr = "[\(Date().toString(.style4))]\(message)"
    NotificationCenter.default.post(name: .xpfSocketLogMessageReceived, object: postStr)
    print(consoleStr)
#endif
}

/// 重要流程日志
func importentLog<T>(_ message:T,file:String = #file,function:String = #function,line:Int = #line){
    let fileName = (file as NSString).lastPathComponent
    //日志内容
    let consoleStr = "[WorkFlowLog][\(Date().toString())]-\(fileName):(\(line))-🏀\(message)⚽️"
    
}

func pp_uuid() -> String{
    let keychain = KeychainSwift()
    
    if let uuid = keychain.get(pp_AppUUIDKeychainNameKey) {
        return uuid.replacingOccurrences(of: "-", with: "")
    }
    let generateStr = UIDevice.current.identifierForVendor?.uuidString ?? ""
    keychain.set(generateStr, forKey: pp_AppUUIDKeychainNameKey)
    
    return generateStr.replacingOccurrences(of: "-", with: "")
}

/// 获取当前网络字符串描述
/// - Returns: description
func pp_networkStr() -> String{
    var network:String?
    if XPApplication.shared.netEnv == .wifi {
        network = "Wifi"
    }else if XPApplication.shared.netEnv == .cellular{
        let networkInfo = CTTelephonyNetworkInfo()
        if let dict = networkInfo.serviceSubscriberCellularProviders {
            for carrier in dict.values {
                if let name = carrier.carrierName {
                    network = name
                    break
                }
            }
        }
        
        if String.isNullOrEmpty(network){
            network = "未知运营商"
        }
        
        /*
         2G
         public let CTRadioAccessTechnologyGPRS: String
         public let CTRadioAccessTechnologyEdge: String
         public let CTRadioAccessTechnologyCDMA1x: String
         
         3G
         public let CTRadioAccessTechnologyWCDMA: String
         public let CTRadioAccessTechnologyHSDPA: String
         public let CTRadioAccessTechnologyHSUPA: String
         public let CTRadioAccessTechnologyCDMAEVDORev0: String
         public let CTRadioAccessTechnologyCDMAEVDORevA: String
         public let CTRadioAccessTechnologyCDMAEVDORevB: String
         public let CTRadioAccessTechnologyeHRPD: String
         
         4G
         public let CTRadioAccessTechnologyLTE: String
         
         5G
         public let CTRadioAccessTechnologyNRNSA: String
         public let CTRadioAccessTechnologyNR: String
         **/
        let currentRadioAccessTechnology = networkInfo.serviceCurrentRadioAccessTechnology?.values.first ?? ""
        switch currentRadioAccessTechnology {
        case CTRadioAccessTechnologyGPRS,CTRadioAccessTechnologyEdge,CTRadioAccessTechnologyCDMA1x:
            network! += "2G"
        case CTRadioAccessTechnologyWCDMA,CTRadioAccessTechnologyHSDPA,CTRadioAccessTechnologyHSUPA,CTRadioAccessTechnologyCDMAEVDORev0,CTRadioAccessTechnologyCDMAEVDORevA,CTRadioAccessTechnologyCDMAEVDORevB,CTRadioAccessTechnologyeHRPD:
            network! += "3G"
        case CTRadioAccessTechnologyLTE:
            network! += "4G"
        default:
            if #available(iOS 14.1, *){
                if currentRadioAccessTechnology == CTRadioAccessTechnologyNRNSA || currentRadioAccessTechnology == CTRadioAccessTechnologyNR {
                    network! += "5G"
                }else{
                    network! += "未知网络"
                }
            }else{
                network! += "未知网络"
            }
        }
    }
    return network ?? "无效网络"
}

/// 获取区间随机数
///
/// - Parameters:
///   - min: 最小数
///   - max: 最大数
/// - Returns: <#return value description#>
func pp_random(_ min:UInt32, _ max:UInt32) -> Int{
    if min == 0 {
        //获取[0-Max]的区间
        return Int(arc4random_uniform(max+1))
    }else if min == 1 {
        //获取[1-Max]的区间
        return Int(arc4random()%max)+1
    }else{
        //获取[Min-Max]的区间
        return Int(arc4random_uniform(max + 1 - min) + min)
    }
}

/// 获取对象指针地址
/// - Parameter value: <#value description#>
/// - Returns: <#description#>
func pp_pointer(_ value:AnyObject) -> UnsafeMutableRawPointer{
   return Unmanaged.passUnretained(value).toOpaque()
}

/**
 获取plist字典
 
 :param: fileName <#fileName description#>
 
 :returns: <#return value description#>
 */
func pp_plist(_ fileName:String) -> NSDictionary {
    let path:String = String(describing: Bundle.main.path(forResource: fileName, ofType: "plist"))
    return NSDictionary.init(contentsOfFile:path)!
}

/// 获取当前应用设置的语言字符串
/// - Parameter key: 唯一标志Key
/// - Returns: <#description#>
func pp_localizedString(_ key:String) -> String{
    //1.获取当前语言Code
    let languageCode = XPApplication.shared.language
    //PPLog("当前展示的语言:\(languageCode)")
    //2.获取本地语言包
    var languageBundle:Bundle?
    var bundlePath = XPSandbox.shared.buildPath(.files, "languages/", "\(languageCode).lproj", true)
    if XPSandbox.shared.isFileExists(bundlePath) {
        languageBundle = Bundle.init(path: bundlePath)
    }else{//没有就用本地默认包
        bundlePath = Bundle.main.path(forResource: "en", ofType: "lproj")!
        languageBundle = Bundle.init(path: bundlePath)
    }
    if var translatedString = languageBundle?.localizedString(forKey: key, value: "", table: nil), translatedString != key {
        //获取的值不一样说明成功获取对应的值
        if translatedString.length < 1 {
            translatedString = NSLocalizedString(key, tableName: nil, bundle: Bundle.main, value: key, comment: key)
        }
        return translatedString
    }else{
        //没有就从本地默认语言中获取
        bundlePath = Bundle.main.path(forResource: "en", ofType: "lproj")!
        languageBundle = Bundle.init(path: bundlePath)
        if var translatedString = languageBundle?.localizedString(forKey: key, value: "", table: nil), translatedString != key {
            if translatedString.length < 1 {
                translatedString = NSLocalizedString(key, tableName: nil, bundle: Bundle.main, value: key, comment: key)
            }
            return translatedString
        }
    }
    //都没有就返回Key
    return key
}

/// 简化CGRect的穿件
///
/// - Parameters:
///   - x: <#x description#>
///   - y: <#y description#>
///   - width: <#width description#>
///   - height: <#height description#>
/// - Returns: <#return value description#>
func pp_frame(_ x:CGFloat = 0, _ y:CGFloat = 0, _ width:CGFloat, _ height:CGFloat) -> CGRect{
    return CGRect(x: x, y: y, width: width, height: height)
}

/// 弹出应用评分/评论，或跳转到AppStore
/// - Parameter appleId: <#appleId description#>
func pp_showAppStoreComment(_ appleId:String){
    /*
     SKStoreReviewController.requestReview() 一年每个用户只有3次机会，其他的都跳到AppStore
     */
    let key = "showAppStoreComment_\(Date().year)_count"
    var toAppStore = true
    let count = UserDefaults.standard.value(forKey: key) as? Int ?? 0
    //if count < 4 && pp_random(0, 200) < 20{
    if count < 4{
        UserDefaults.standard.setValue(count+1, forKey: key)
        toAppStore = false
    }
    if toAppStore {
        let urlStr = "itms-apps://itunes.apple.com/app/id\(AppleID)?action=write-review"
        if let url = URL(string: urlStr) {
            UIApplication.shared.open(url, options: [:], completionHandler: nil)
        }
    }else{
        SKStoreReviewController.requestReview()
    }
}

/// 获取当前窗体
///
/// - Returns: <#return value description#>
func pp_keyWindow() -> UIWindow?{
    var window:UIWindow?
    //if isPhone {
        if #available(iOS 13.0, *){
            for scene in UIApplication.shared.connectedScenes {
                if scene.activationState == .foregroundActive {
                    let winScene = scene as? UIWindowScene
                    if #available(iOS 15.0, *){
                        window = winScene?.keyWindow
                    }else{
                        window = winScene?.windows.first
                    }
                    break
                }
            }
        }else{
            window = UIApplication.shared.keyWindow
        }
    //}else if isPad {
        
    //}
    return window
}

/// 获取顶层控制器
/// - Returns: <#description#>
func pp_topViewController() -> XPBaseViewController?{
    return UIApplication.topViewController() as? XPBaseViewController
}

/// 获取本地资源
///
/// - Parameters:
///   - name: <#name description#>
///   - type: <#type description#>
/// - Returns: <#return value description#>
func pp_BundleMainPath(_ name:String,_ type:String) -> String?{
    if let filePath = Bundle.main.path(forResource: name, ofType: type){
        return filePath
    }
    return nil
}

/// 获取StoryBoard控制器
/// - Parameters:
///   - storyBoardName: 故事板文件名
///   - viewControllerIdentifier: 故事板中的ViewController的storyBoard ID
/// - Returns: <#description#>
func pp_storyBoard(_ storyBoardName:String, _ viewControllerIdentifier:String) -> UIViewController {
    let storyboard = UIStoryboard(name: storyBoardName, bundle: nil)
    let vc = storyboard.instantiateViewController(withIdentifier: viewControllerIdentifier)
    return vc
}

/// 创建颜色渐变layer
///
/// - Parameters:
///   - isVerical: <#isVerical description#>
///   - rect: <#rect description#>
///   - startColor: <#startColor description#>
///   - endColor: <#endColor description#>
/// - Returns: <#return value description#>
func pp_gradientlayer_create(_ isVerical:Bool = false, _ rect:CGRect, _ startColor: UIColor = UIColor.black, _ endColor: UIColor = UIColor.black.withAlphaComponent(0.5)) -> CAGradientLayer{
    let gradientLayer = CAGradientLayer()
    gradientLayer.frame = rect
    gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
    gradientLayer.startPoint = CGPoint(x: 0, y: 0)
    if isVerical {
        gradientLayer.endPoint = CGPoint(x: 0, y: 1)
    }else{
        gradientLayer.endPoint = CGPoint(x: 1, y: 0)
    }
    gradientLayer.locations = [0,1]
    return gradientLayer
}

/// 通过数字获取转换后的字符串
///
/// - Parameter count: <#count description#>
/// - Returns: 大于1亿：N亿+， 大于1万，N万+，大于1千：NK+
func pp_str_countunit(_ count:Int?) -> String{
    var str = ""
    if let number = count {
        if number > 100000000 {
            str = "\(Int(number / 100000000))亿+"
        }else if number > 10000 {
            str = "\(Int(number / 10000))万+"
        }else if number < 0 {
            str = "0"
        }else{
            str = "\(number)"
        }
    }
    return str
}

/// 获取动态颜色
/// - Parameters:
///   - mode: 外观模式
///   - light: 浅色
///   - dark: 深色
/// - Returns: <#description#>
func pp_color(_ mode:ThemeMode, _ light:UIColor, _ dark:UIColor) -> UIColor{
    if mode == .light {
        return light
    }else if mode == .dark {
        return dark
    }else{
        if #available(iOS 13.0, *) {
            return UIColor(dynamicProvider: { (trait) -> UIColor in
                if trait.userInterfaceStyle == .dark {
                    return dark
                }else{
                    return light
                }
            })
        } else {
            return light
        }
    }
}

/// 将图片集合合并成一个正方形图片
/// - Parameters:
///   - size: <#size description#>
///   - images: <#images description#>
func pp_image_mergeImagesSquare(_ size:CGSize, _ images:[String]?) -> UIImage?{
    
    return UIImage()
}

/// 弹出框动画效果显示
/// - Parameter alert: <#alert description#>
func pp_alert_show(_ alert: UIView?) {
    let animation = CAKeyframeAnimation(keyPath: "transform")
    animation.duration = CFTimeInterval(0.3)
    var values = [AnyHashable]()
    values.append(NSValue(caTransform3D: CATransform3DMakeScale(0.1, 0.1, 1.0)))
    values.append(NSValue(caTransform3D: CATransform3DMakeScale(1.2, 1.2, 1.0)))
    values.append(NSValue(caTransform3D: CATransform3DMakeScale(0.9, 0.9, 1.0)))
    values.append(NSValue(caTransform3D: CATransform3DMakeScale(1.0, 1.0, 1.0)))
    animation.values = values
    alert?.layer.add(animation, forKey: nil)
}

/// 弹出框动画效果隐藏
/// - Parameter alert: <#alert description#>
func pp_alert_hide(_ alert: UIView?){
    UIView.animate(withDuration: 0.3, animations: {() -> Void in
        alert?.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
        alert?.backgroundColor = UIColor.clear
        alert?.alpha = 0
    }, completion: {(_ finished: Bool) -> Void in
        alert?.superview?.removeFromSuperview()
    })
}


/// 清除所有webview的缓存数据
func pp_clear_webView_caches(){
    WKWebsiteDataStore.default().removeData(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(), modifiedSince: Date.init(timeIntervalSince1970: 0)) {
        PPLog("清苦WKWebView所有本地缓存数据")
    }
}

/// 动态实例化ViewController,并赋值参数
/// - Parameters:
///   - className: 控制器名称
///   - params: 参数根据控制里的参数给,["参数名":参数值]
func pp_init_vc(_ className:String, _ params:[String:Any]? = nil) -> UIViewController?{
    let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String
    if let viewController = NSClassFromString(namespace + "." + className) as? UIViewController.Type {
        let vc = viewController.init()
    
        guard let params = params, params.keys.count > 0 else{ return vc }
        
        //获取控制器的所有属性, 属性一定要在className文件中声明，父类声明无效
        var propertyNames = [String]()
        var count: UInt32 = 0
        let cls: AnyClass? = object_getClass(vc)
        let plist = class_copyPropertyList(cls, &count)
        for item in 0..<count {
            let property = plist?[Int(item)]
            let cname = property_getName(property!);
            let name = String(cString: cname);
            //print("property：<\(name)>")
            propertyNames.append(name)
        }
    
        //属性存在才进行赋值
        for key in params.keys {
            if !propertyNames.contains(key){
                continue
            }
            //一定要按照提供的参数规则传参且一定要有，否则此处会闪退，尝试docatch好像没什么卵用
            vc.setValue(params[key], forKey: key)
        }
        return vc
    }
    return nil
}

//MARK: JSON对象和数组、Data、String转换
/// JSON字典对象转Data对象
///
/// - Parameter jsonDic: <#jsonDic description#>
/// - Returns: <#return value description#>
func pp_json_toData(_ jsonDic:[String: Any]) -> Data? {
    if (!JSONSerialization.isValidJSONObject(jsonDic)) {
        PPLog("is not a valid json object")
        return nil
    }
    //利用自带的json库转换成Data
    //如果设置options为JSONSerialization.WritingOptions.prettyPrinted，则打印格式更好阅读
    let data = try? JSONSerialization.data(withJSONObject: jsonDic, options: [])
    //Data转换成String打印输出
    //let str = String(data:data!, encoding: String.Encoding.utf8)
    //输出json字符串
    //PPLog("Json Str:\(str!)")
    return data
}

/// JSON字典对象转字符串
///
/// - Parameter jsonDic: <#jsonDic description#>
/// - Returns: <#return value description#>
func pp_json_toString(_ jsonDic:[String: Any]) -> String? {
    let data = pp_json_toData(jsonDic)
    return String(data:data!, encoding: String.Encoding.utf8)
}

/// JSON Data对象转JSON字典对象
///
/// - Parameter data: <#data description#>
/// - Returns: <#return value description#>
func pp_json_fromData(_ data:Data) ->[String: Any]?{
    let json = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any]
    return json
}

/// JSON 字符串转JSON字典对象
///
/// - Parameter jsonStr: <#jsonStr description#>
/// - Returns: <#return value description#>
func pp_json_fromString(_ jsonStr:String) -> [String: Any]?{
    if jsonStr.isNullOrEmpty() { return nil}
    let data = jsonStr.data(using: .utf8)
    return pp_json_fromData(data!)
}

func pp_jsonarray_fromString(_ jsonStr:String) -> [[String: Any]]?{
    if jsonStr.isNullOrEmpty() { return nil}
    guard let data = jsonStr.data(using: .utf8) else{ return nil }
    let json = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [[String: Any]]
    return json
}

//MARK: GCD操作
/// GCD定时器倒计时
///   - timeInterval: 循环间隔时间
///   - repeatCount: 重复次数
///   - handler: 循环事件, 闭包参数： 1. timer， 2. 剩余执行次数
func pp_dispatchTimer(timeInterval: Double, repeatCount:Int, handler:@escaping (DispatchSourceTimer?, Int)->()){
    if repeatCount <= 0 {
        return
    }
    let timer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main)
    var count = repeatCount
    timer.schedule(wallDeadline: .now(), repeating: timeInterval)
    timer.setEventHandler(handler: {
        count -= 1
        DispatchQueue.main.async {
            handler(timer, count)
        }
        if count == 0 {
            timer.cancel()
        }
    })
    timer.resume()
}

/// GCD定时器循环操作
///   - timeInterval: 循环间隔时间
///   - handler: 循环事件
func pp_dispatchTimer(timeInterval: Double, handler:@escaping (DispatchSourceTimer?)->()){
    let timer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main)
    timer.schedule(deadline: .now(), repeating: timeInterval)
    timer.setEventHandler {
        DispatchQueue.main.async {
            handler(timer)
        }
    }
    timer.resume()
}

/// GCD延时操作
///   - after: 延迟的时间
///   - handler: 事件
func pp_dispatchAfter(after: Double, handler:@escaping ()->()) {
    DispatchQueue.main.asyncAfter(deadline: .now() + after) {
        handler()
    }
}


/// 全局串行队列异步
/// - Parameter handler: <#handler description#>
/// - Returns: <#description#>
func pp_dispatchMain(handler:@escaping () -> ()){
    DispatchQueue.main.async {
        handler()
    }
}

/// 全局并行队列异步
/// - Parameters:
///   - qos: <#qos description#>
///   - handler: <#handler description#>
/// - Returns: <#description#>
func pp_dispatchGlobal(qos:DispatchQoS.QoSClass = .default, handler:@escaping () -> ()){
    /*
     public static let background: DispatchQoS
     public static let utility: DispatchQoS
     public static let `default`: DispatchQoS
     public static let userInitiated: DispatchQoS
     public static let userInteractive: DispatchQoS
     public static let unspecified: DispatchQoS
     优先级由最低的background到最高的userInteractive共五个，还有一个为定义的unspecified.
     background：最低优先级，等同于DISPATCH_QUEUE_PRIORITY_BACKGROUND. 用户不可见，比如：在后台存储大量数据
     utility：优先级等同于DISPATCH_QUEUE_PRIORITY_LOW，可以执行很长时间，再通知用户结果。比如：下载一个大文件，网络，计算
     default：默认优先级,优先级等同于DISPATCH_QUEUE_PRIORITY_DEFAULT，建议大多数情况下使用默认优先级
     userInitiated：优先级等同于DISPATCH_QUEUE_PRIORITY_HIGH,需要立刻的结果
     **/
    DispatchQueue.global(qos: qos).async {
        handler()
    }
}

//MARK: 沙盒路径
/// 本地沙盒 #文档# 路径
let pp_sandbox_documentPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first!
/// 本地沙盒 #缓存# 路径
let pp_sandbox_cachePath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first!
/// 本地沙盒 #临时文件# 路径
let pp_sandbox_tempPath = NSTemporaryDirectory()

//MARK: 7.常用枚举

/// 当前运行环境
enum RunEnv:Int{
    case release = 1
    case beta = 2
    case test = 3
    case develop = 4
    case custom = 5
}

/// 网络环境
enum NetEnv:String{
    case unavailable = "无效网络"
    case wifi = "无线网络"
    case cellular = "移动网络"
}

/// 键盘输入类型
enum KeyboardInputType {
    /// 普通输入
    case normal
    /// 表情输入
    case face
}

/**
 本地语言枚举
 
 - en:      英文
 - zh_Hans: 简体中文
 - zh_Hant: 繁体中文
 */
enum LanguageType:String{
    case en = "en"
    case zh_Hans = "zh-Hans"
    case zh_Hant = "zh-Hant"
}

/// 登录类型
///
/// - unkown: 游客登录
/// - byCode: 通过手机验证码登录
/// - byPassword: 通过密码登录
/// - byWechat: 通过微信登录
/// - byWeibo: 通过微博登录
/// - byQQ: 通过QQ登录
/// - byFacebook: 通过Facebook登录
/// - byApple: 通过AppleID登录
/// - byGoogle: 通过Google登录
/// - byOneKey: 通过一键登录
enum LoginType:String {
    case unkown = "游客"
    case byCode = "手机验证码"
    case byPassword = "用户名密码"
    case byWechat = "微信"
    case byWeibo = "微博"
    case byQQ = "QQ"
    case byFacebook = "Facebook"
    case byAppleAcount = "appleAcount"
    case byGoogle = "googleAcount"
    case byOneKey = "一键登录"
}

/// 主题模式
enum ThemeMode:String {
    case `default` = "ME_ThemeMode_Sys"
    case light = "ME_ThemeMode_Light"
    case dark = "ME_ThemeMode_Dark"
}

/// 提醒设置模式
enum MsgWarnMode:Int {
    /// "全部消息"
    case `default` = 1
    /// 仅@我
    case atMe = 2
    /// 仅赞我
    case likeMe = 3
    /// 关闭
    case close = 4
}

/// 设置支付密码跳转方式来源
enum PushPSDSource:Int {
    /// 默认正常push返回 账号安全首页
    case `default` = 0
    /// 忘记密码
    case fromForgetPSD = 1
    /// 身份验证
    case fromAuthentication = 2
}

///  跳转方式来源
enum PushSource:Int {
    /// 默认正常push返回
    case `default` = 0
    /// 程序启动进入详情，返回到根目录
    case fromMain = 1
    /// 欢迎广告页push，返回
    case fromWelcome = 2
    /// 来自搜索
    case fromSearch = 3
}

/// 图片参数枚举
enum ImageParam {
    /// 没有参数
    case none
    /// 将图片缩略成宽度是[W], 并且保存成渐进显示的 jpg 格式
    case interlace
    /// 将图片缩略为宽度200，并且进行模糊半径为3，标准差为2的模糊处理。
    case blur
    /// 圆图，非圆角部分透明，[R]为半径
    case circle
    /// 单边等比缩放，通过宽度等比缩放
    case resizeByWidth
    /// 固定宽高，自动裁剪，将图自动裁剪成宽度为[W]px，高度为[H]px 的效果图
    case resizeByFillWH
    /// 按比例缩放 ,倍数百分比。小于100为缩小，大于100为放大。下面示例将图按比例缩放到原来的1/2
    case resizeByScale
}

//MARK: 8.自定义消息通知
extension Notification.Name {
    /// 键盘即将显示通知
    static let xpfKeyboardWillShow = UIResponder.keyboardWillShowNotification
    /// 江畔已经显示通知
    static let xpfKeyboardDidShow = UIResponder.keyboardDidShowNotification
    /// 键盘即将隐藏通知
    static let xpfKeyboardWillHide = UIResponder.keyboardWillHideNotification
    ///键盘已经隐藏通知
    static let xpfKeyboardDidHide = UIResponder.keyboardDidHideNotification
    /// 同意隐私政策
    static let xpfPrivacyAgreed = Notification.Name("xpfPrivacyAgreed")
    /// 登录状态改变
    static let xpfLoginStatusChanged = Notification.Name("xpfLoginStatusChanged")
    /// 语言切换改变
    static let xpfLanguageChanged = Notification.Name("xpfLanguageChanged")
    /// 网络状态改变
    static let xpfNetworkChanged = Notification.Name("xpfNetworkChanged")
    /// 请求无权限改变
    static let xpfAccessDeniedChanged = Notification.Name("xpfAccessDeniedChanged")
    /// 个人信息改变
    static let xpfProfileChanged = Notification.Name("xpfProfileChanged")
    /// 刷新状态改变
    static let xpfRefreshStateChanged = Notification.Name("xpfRefreshStateChanged")
    /// 阅读模式改变
    static let xpfReadModeChanged = Notification.Name("xpfReadModeChanged")
    /// 字体模式改变
    static let xpfFontModeChanged = Notification.Name("xpfFontModeChanged")
    /// 主题模式改变
    static let xpfThemeModeChanged = Notification.Name("xpfFontModeChanged")
    /// 通知设置改变
    static let xpfNotifySettingChanged = Notification.Name("xpfNotifySettingChanged")
    /// 定位区域改变
    static let xpfLocationRegionChanged = Notification.Name("xpfLocationRegionChanged")
    /// 导航栏改变
    static let xpfNavBarChanged = Notification.Name("xpfNavBarChanged")
    /// 选项卡栏改变
    static let xpfTabBarChanged = Notification.Name("xpfTabBarChanged")
    /// 当前城市改变
    static let xpfCurrentCityChanged = Notification.Name("xpfCurrentCityChanged")
    /// 评论列表改变
    static let xpfCommentListChanged = Notification.Name("xpfCommentListChanged")
    /// 支付成功
    static let xpfPaySuccess = Notification.Name("xpfPaySuccess")
    /// 支付失败
    static let xpfPayFailed = Notification.Name("xpfPayFailed")
    /// Socket状态变更
    static let xpfSocketStatusChanged = Notification.Name("xpfSocketStatusChanged")
    /// Socket新消息已接收
    static let xpfSocketNewMsgReceived = Notification.Name("xpfSocketNewMsgReceived")
    /// Socket日志信息
    static let xpfSocketLogMessageReceived = Notification.Name("xpfSocketLogMessageReceived")
    /// 通知基类滚动启动/禁用状态改变
    static let xpfBaseScrollViewEnabledNotify = Notification.Name("xpfBaseScrollViewEnabledNotify")
    /// 通知child类滚动启动/禁用状态改变
    static let xpfSubScrollViewEnabledNotify = Notification.Name("xpfSubScrollViewEnabledNotify")
}
